import { Query } from "../lib/http/query";
import { useCallback, useEffect, useReducer } from "react";
type PickPromise<P> = P extends Promise<infer R> ? R : P
type Querys = { [k in string]: Query }
type QueryResult<T extends Querys> = {
    [k in keyof T]: {
        loading: boolean,
        result: PickPromise<ReturnType<T[k]["commit"]>>,
        error: any
    }
} & {
    commit: (key: keyof T) => (callback: (http: T[typeof key]) => T[typeof key]) => void
}
type WithPage = <T extends Querys>(
    Component: (props: QueryResult<T>) => JSX.Element,
    querys?: T
) => (() => JSX.Element)
export const useError = (effect: React.EffectCallback, dep: any) => {
    useEffect(() => {
        if (!dep) return;
        return effect()
    }, [dep]);
}
type ActionType = {
    type: "data" | "loading" | "error",
    data?: any,
    key: string
}
const withPage: WithPage = (
    Component,
    query = {} as any
) => {
    const Page = () => {
        const [state, dispatch] = useReducer((state: any, action: ActionType) => {
            state[action.key].loading = false;
            switch (action.type) {
                case "data":
                    state[action.key].result = action.data
                    break;
                case "loading":
                    state[action.key].loading = true;
                    break;
                case "error":
                    state[action.key].error = action.data;
                    break;
            }
            return { ...state }
        }, Object.keys(query).reduce((acc, key) => {
            acc[key] = {
                result: query[key].getInitialValue(),
                err: null,
                loading: true
            }
            return acc;
        }, {} as any));
        const commit = useCallback((key: string) => {
            return (
                callback: any = (e: any) => e
            ) => {
                const http = callback(query[key]);
                dispatch({
                    key,
                    type: "loading"
                })
                http.commit((error: any, result: any) => {
                    if (!!error) {
                        dispatch({
                            key,
                            type: "error",
                            data: error
                        })
                    } else {
                        dispatch({
                            key,
                            data: result,
                            type: "data"
                        })
                    }
                }, []);
            }
        }, []);
        useEffect(() => {
            for (let key in query) {
                commit(key)((e: any) => e)
            }
        }, [commit]);
        return (
            <Component
                {...state}
                commit={commit}
            />
        )
    }
    return Page
}
export default withPage